home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / smtpserv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-20  |  24.1 KB  |  1,030 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  */
  4. #include <stdio.h>
  5. #include <time.h>
  6. #ifdef UNIX
  7. #include <sys/types.h>
  8. #endif
  9. #if    defined(__STDC__) || defined(__TURBOC__)
  10. #include <stdarg.h>
  11. #endif
  12. #include <ctype.h>
  13. #include <setjmp.h>
  14. #include "global.h"
  15. #include "config.h"
  16. #include "mbuf.h"
  17. #include "cmdparse.h"
  18. #include "socket.h"
  19. #ifdef LZW
  20. #include "lzw.h"
  21. #endif
  22. #include "iface.h"
  23. #include "proc.h"
  24. #include "smtp.h"
  25. #include "commands.h"
  26. #include "dirutil.h"
  27. #include "mailbox.h"
  28. #include "bm.h"
  29. #ifdef NNTP
  30. #include "nntp.h"
  31. #endif
  32. #ifdef STATUS        /* value not used - keep lint happy */
  33. #undef STATUS
  34. #endif
  35. #include "domain.h"
  36. #include "files.h"
  37.  
  38. static char *Days[7] = {  "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  39. char *Months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
  40.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  41.  
  42. static struct list * near expandalias __ARGS((struct list **head,char *user));
  43. static int near getmsgtxt __ARGS((struct smtpsv *mp));
  44. static struct smtpsv * near mail_create __ARGS((void));
  45. static void near mail_clean __ARGS((struct smtpsv *mp));
  46. static int near mailit __ARGS((FILE *data,char *from,struct list *tolist));
  47. static int near router_queue __ARGS((FILE *data,char *from,struct list *to));
  48. static void near smtplog __ARGS((char *fmt,...));
  49. static void smtpserv __ARGS((int s,void *unused,void *p));
  50. static int near mailuser __ARGS((FILE *data,char *from,char *to));
  51.  
  52. /* Reply messages */
  53. static char Help[] = "214-Commands:\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET EXPN\n214 End\n";
  54. static char SmtpBanner[] = "220 %s SMTP ready\n";
  55. static char Closing[] = "221 Closing\n";
  56. static char Ok[] = "250 Ok\n";
  57. static char Reset[] = "250 Reset state\n";
  58. static char Sent[] = "250 Sent\n";
  59. static char Ourname[] = "250 Hello, pleased to meet you\n";
  60. #ifdef LZW
  61. static char LZWOk[] = "252 LZW Ok\n";
  62. #endif
  63. static char Enter[] = "354 Enter mail, end with .\n";
  64. static char Lowmem[] = "421 System overloaded\n";
  65. static char Ioerr[] = "452 Temp file write error\n";
  66. static char Badcmd[] = "500 Command unrecognized\n";
  67. static char Syntax[] = "501 Syntax error\n";
  68. static char Needrcpt[] = "503 Need RCPT (recipient)\n";
  69. static char Unknown[] = "550 <%s> address unknown\n";
  70. static char Noalias[] = "550 No alias for <%s>\n";
  71.  
  72. static int Ssmtp = -1; /* prototype socket for service */
  73.  
  74. /* Start up SMTP receiver service */
  75. int
  76. smtp1(int argc,char *argv[],void *p)
  77. {
  78.     struct sockaddr_in lsocket;
  79.     int s;
  80.  
  81.     if(Ssmtp != -1){
  82.         return 0;
  83.     }
  84.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  85.     chname(Curproc,"SMTP listener");
  86.  
  87.     lsocket.sin_family = AF_INET;
  88.     lsocket.sin_addr.s_addr = INADDR_ANY;
  89.     lsocket.sin_port = (argc < 2) ? IPPORT_SMTP : atoi(argv[1]);
  90.  
  91.     Ssmtp = socket(AF_INET,SOCK_STREAM,0);
  92.     bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
  93.     listen(Ssmtp,1);
  94.     for(;;){
  95.         if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
  96.             break;    /* Service is shutting down */
  97.  
  98.         if(availmem() < Memthresh){
  99.             usputs(s,Lowmem);
  100.             shutdown(s,1);
  101.         } else {
  102.             /* Spawn a server */
  103.             newproc("SMTP server",2816,smtpserv,s,NULL,NULL,0);
  104.         }
  105.     }
  106.     return 0;
  107. }
  108.  
  109. /* Shutdown SMTP service (existing connections are allowed to finish) */
  110. int
  111. smtp0(int argc,char *argv[],void *p)
  112. {
  113.     close_s(Ssmtp);
  114.     Ssmtp = -1;
  115.     return 0;
  116. }
  117.  
  118. static void
  119. smtpserv(int s,void *unused,void *p)
  120. {
  121.     struct smtpsv *mp;
  122.     char **cmdp,*arg,*cp,*cmd,*newaddr,buf[LINELEN];
  123.     struct list *ap,*list;
  124.     int cnt;
  125.     char address_type;
  126. #ifdef LZW
  127.     extern int Smtplzw;
  128.     int lzwbits, lzwmode;
  129. #endif
  130.  
  131.     /* Command table */
  132.     char *commands[] = {
  133.         "helo",
  134.     #define    HELO_CMD    0
  135.         "noop",
  136.     #define    NOOP_CMD    1
  137.         "mail from:",
  138.     #define    MAIL_CMD    2
  139.         "quit",
  140.     #define    QUIT_CMD    3
  141.         "rcpt to:",
  142.     #define    RCPT_CMD    4
  143.         "help",
  144.     #define    HELP_CMD    5
  145.         "data",
  146.     #define    DATA_CMD    6
  147.         "rset",
  148.     #define    RSET_CMD    7
  149.         "expn",
  150.     #define EXPN_CMD    8
  151.         "xlzw",
  152.     #define XLZW_CMD    9
  153.         NULLCHAR
  154.     };
  155.  
  156.     sockmode(s,SOCK_ASCII);
  157.     sockowner(s,Curproc);        /* We own it now */
  158.     log(s,"SMTP open");
  159.  
  160.     if((mp = mail_create()) == NULLSMTPSV){
  161.         tputs(Nospace);
  162.         goto quit;
  163.     }
  164.     mp->s = s;
  165.  
  166.     usprintf(mp->s,SmtpBanner,Hostname);
  167. loop:
  168.     if ((cnt = recvline(s,buf,LINELEN)) == -1) {
  169.         /* He closed on us */
  170.         goto quit;
  171.     }
  172.     if(cnt < 4){
  173.         /* Can't be a legal command */
  174.         usputs(mp->s,Badcmd);
  175.         goto loop;
  176.     }
  177.     rip(buf);
  178.     cmd = buf;
  179.  
  180.     /* Translate entire buffer to lower case */
  181.     for(cp = cmd;*cp != '\0';cp++)
  182.         *cp = tolower(*cp);
  183.  
  184.     /* Find command in table; if not present, return syntax error */
  185.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  186.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  187.             break;
  188.     if(*cmdp == NULLCHAR){
  189.         usputs(mp->s,Badcmd);
  190.         goto loop;
  191.     }
  192.     arg = &cmd[strlen(*cmdp)];
  193.     /* Skip spaces after command */
  194.     while(*arg == ' ')
  195.         arg++;
  196.     /* Execute specific command */
  197.     switch((int)(cmdp - commands)) {
  198.     case XLZW_CMD:
  199. #ifdef LZW
  200.         if(Smtplzw) {
  201.             char *cp1;
  202.             lzwmode = lzwbits = 0;
  203.  
  204.             cp1 = strchr(arg,' ');
  205.             *cp1 = '\0';
  206.             lzwbits = atoi(arg);
  207.             cp1++;
  208.             lzwmode = atoi(cp1);
  209.             if(lzwbits > lzwmode) {
  210.                 usputs(mp->s,LZWOk);
  211.                 lzwinit(mp->s,lzwbits,lzwmode);
  212.             } else {
  213.                 usputs(mp->s,Badcmd);
  214.             }
  215.         } else {
  216.             usputs(mp->s,Badcmd);
  217.         }
  218. #else
  219.         usputs(mp->s,Badcmd);
  220. #endif
  221.         break;
  222.     case HELO_CMD:
  223.         xfree(mp->system);
  224.         mp->system = strxdup(arg);
  225.         usputs(mp->s,Ourname);
  226.         break;
  227.     case NOOP_CMD:
  228.         usputs(mp->s,Ok);
  229.         break;
  230.     case MAIL_CMD:
  231.         if((cp = getname(arg)) == NULLCHAR){
  232.             usputs(mp->s,Syntax);
  233.             break;
  234.         }
  235.         xfree(mp->from);
  236.         mp->from = strxdup(cp);
  237.         usputs(mp->s,Ok);
  238.         break;
  239.     case QUIT_CMD:
  240.         usputs(mp->s,Closing);
  241.         goto quit;
  242.     case RCPT_CMD:    /* Specify recipient */
  243.         if((cp = getname(arg)) == NULLCHAR){
  244.             usputs(mp->s,Syntax);
  245.             break;
  246.         }
  247.         /* rewrite address if possible */
  248.         if((newaddr = rewrite_address(cp)) != NULLCHAR) {
  249.             strcpy(buf,newaddr);
  250.             cp = buf;
  251.             xfree(newaddr);
  252.         }
  253.         /* check if address is ok */
  254.         if ((address_type = validate_address(cp)) == BADADDR) {
  255.             usprintf(mp->s,Unknown,cp);
  256.             break;
  257.         }
  258.         /* if a local address check for an alias */
  259.         if (address_type == LOCAL) {
  260.             expandalias(&mp->to, cp);
  261.         } else {
  262.             /* a remote address is added to the list */
  263.             addlist(&mp->to, cp, address_type);
  264.         }
  265.         usputs(mp->s,Ok);
  266.         break;
  267.     case HELP_CMD:
  268.         usputs(mp->s,Help);
  269.         break;
  270.     case DATA_CMD:
  271.         if(mp->to == NULLLIST)
  272.             usputs(mp->s,Needrcpt);
  273.         else if ((mp->data = temp_file(0,1)) == NULLFILE)
  274.             usputs(mp->s,Ioerr);
  275.         else
  276.             getmsgtxt(mp);
  277.         break;
  278.     case RSET_CMD:
  279.         del_list(mp->to);
  280.         mp->to = NULLLIST;
  281.         usputs(mp->s,Reset);
  282.         break;
  283.     case EXPN_CMD:
  284.         if (*arg == '\0') {
  285.             usputs(mp->s,Syntax);
  286.             break;
  287.         }
  288.         list = NULLLIST;
  289.         /* rewrite address if possible */
  290.         if((newaddr = rewrite_address(arg)) != NULLCHAR)
  291.             if(strcmp(newaddr,arg) == 0) {
  292.                 xfree(newaddr);
  293.                 newaddr = NULLCHAR;
  294.             } else {
  295.                 strcpy(buf,newaddr);
  296.                 arg = buf;
  297.             }
  298.         list = NULLLIST;
  299.         newaddr = NULLCHAR;
  300.         expandalias(&list,arg);
  301.         if (strcmp(list->val,arg) == 0 && list->next == NULLLIST)
  302.             if(newaddr == NULLCHAR) {
  303.                 usprintf(mp->s,Noalias,arg);
  304.                 del_list(list);
  305.                 break;
  306.             }
  307.         ap = list;
  308.         while (ap->next != NULLLIST) {
  309.             usprintf(mp->s,"250-%s\n",ap->val);
  310.             ap = ap->next;
  311.         }
  312.         usprintf(mp->s,"250 %s\n",ap->val);
  313.         del_list(list);
  314.         xfree(newaddr);
  315.         break;
  316.     }
  317.     goto loop;
  318.  
  319. quit:
  320.     log(s,"SMTP close");
  321.     close_s(s);
  322.     if(mp != NULLSMTPSV) {
  323.         mail_clean(mp);
  324.         smtptick(NULL);            /* start SMTP daemon immediately */
  325.     }
  326. }
  327.  
  328. /* read the message text */
  329. static int near
  330. getmsgtxt(struct smtpsv *mp)
  331. {
  332.     char buf[LINELEN];
  333.     char *p = buf;
  334.  
  335.     /* Add timestamp; ptime adds newline */
  336.     fputs("Received: ",mp->data);
  337.     if(mp->system != NULLCHAR)
  338.         fprintf(mp->data,"from %s ",mp->system);
  339.     fprintf(mp->data,"by %s with SMTP\n\tid AA%ld; %s",
  340.             Hostname, get_msgid(), ptime(&currtime));
  341.     if(ferror(mp->data)){
  342.         usputs(mp->s,Ioerr);
  343.         return 1;
  344.     } else {
  345.         usputs(mp->s,Enter);
  346.     }
  347.     while(1) {
  348.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  349.             return 1;
  350.         }
  351.         rip(p);
  352.         /* check for end of message ie a . or escaped .. */
  353.         if (*p == '.') {
  354.             if (*++p == '\0') {
  355.                 /* Also sends appropriate response */
  356.                 if (mailit(mp->data,mp->from,mp->to) != 0)
  357.                     usputs(mp->s,Ioerr);
  358.                 else
  359.                     usputs(mp->s,Sent);
  360.                 fclose(mp->data);
  361.                 mp->data = NULLFILE;
  362.                 del_list(mp->to);
  363.                 mp->to = NULLLIST;
  364.                 return 0;
  365.             } else if (!(*p == '.' && *(p+1) == '\0'))
  366.                 p--;
  367.         }
  368.         /* for UNIX mail compatiblity */
  369.         if (strncmp(p,"From ",5) == 0)
  370.             putc('>',mp->data);
  371.         /* Append to data file */
  372.         if(fprintf(mp->data,"%s\n",p) < 0) {
  373.             usputs(mp->s,Ioerr);
  374.             return 1;
  375.         }
  376.     }
  377. }
  378.  
  379. /* Create control block, initialize */
  380. static struct smtpsv * near
  381. mail_create()
  382. {
  383.     struct smtpsv *mp = (struct smtpsv *)mxallocw(sizeof(struct smtpsv));
  384.     mp->from = strxdup("");    /* Default to null From address */
  385.     return mp;
  386. }
  387.  
  388. /* Free resources, delete control block */
  389. static void near
  390. mail_clean(struct smtpsv *mp)
  391. {
  392.     if (mp == NULLSMTPSV)
  393.         return;
  394.     xfree(mp->system);
  395.     xfree(mp->from);
  396.     if(mp->data != NULLFILE)
  397.         fclose(mp->data);
  398.     del_list(mp->to);
  399.     xfree((char *)mp);
  400. }
  401.  
  402.  
  403. /* Given a string of the form <user@host>, extract the part inside the
  404.  * brackets and return a pointer to it.
  405.  */
  406. char *
  407. getname(char *cp)
  408. {
  409.     char *cp1;
  410.  
  411.     if ((cp = strchr(cp,'<')) == NULLCHAR)
  412.         return NULLCHAR;
  413.     cp++;    /* cp -> first char of name */
  414.     if ((cp1 = strchr(cp,'>')) == NULLCHAR)
  415.         return NULLCHAR;
  416.     *cp1 = '\0';
  417.     return cp;
  418. }
  419.  
  420. /* General mailit function. It takes a list of addresses which have already
  421. ** been verified and expanded for aliases. Base on the current mode the message
  422. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  423. */
  424. static int near
  425. mailit(data,from,tolist)
  426. FILE *data;
  427. char *from;
  428. struct list *tolist;
  429. {
  430.     struct list *ap, *dlist = NULLLIST;
  431.     register FILE *fp;
  432.     char mailbox[LINELEN], *cp, *host, *qhost;
  433.     int    c, fail = 0;
  434.     extern int16 Smtpquiet;
  435.  
  436.     if ((Smtpmode & QUEUE) != 0)
  437.         return(router_queue(data,from,tolist));
  438.  
  439.     do {
  440.         qhost = NULLCHAR;
  441.         for(ap = tolist;ap != NULLLIST;ap = ap->next) {
  442.             if (ap->type == DOMAIN) {
  443.                 if ((host = strrchr(ap->val,'@')) != NULLCHAR)
  444.                     host++;
  445.                 else
  446.                     host = Hostname;
  447.                 if(qhost == NULLCHAR)
  448.                     qhost = host;
  449.                 if(stricmp(qhost,host) == 0) {
  450.                     ap->type = BADADDR;
  451.                     addlist(&dlist,ap->val,0);
  452.                 }
  453.             }
  454.         }
  455.         if(qhost != NULLCHAR) {
  456.             rewind(data);
  457.             queuejob(data,qhost,dlist,from);
  458.             del_list(dlist);
  459.             dlist = NULLLIST;
  460.         }
  461.     } while(qhost != NULLCHAR);
  462.  
  463.    /*-------------------------------------------------------------------*
  464.    * dk5dc, process nntp entries !xxxx....                              *
  465.    *--------------------------------------------------------------------*/
  466. #ifdef NNTP
  467.    for(ap = tolist;ap != NULLLIST;ap = ap->next) {
  468.       if (ap->type != NNTP_GATE)
  469.          continue;
  470.       /*----------------------------------------------------------------*
  471.       * start processing                                                *
  472.       *-----------------------------------------------------------------*/
  473.       nnGpost(data,from,ap);
  474.       ap->type = BADADDR;
  475.    }
  476.  
  477. #endif
  478.  
  479.     for(ap = tolist;ap != NULLLIST;ap = ap->next) {
  480.         if(ap->type != LOCAL) {
  481.             ap->type = DOMAIN;
  482.             continue;
  483.         }
  484.         rewind(data);
  485.         /* strip off host name of LOCAL addresses */
  486.         if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  487.             *cp = '\0';
  488.  
  489.         /* truncate long user names */
  490.         if (strlen(ap->val) > MBOXLEN)
  491.             ap->val[MBOXLEN] = '\0';
  492.  
  493.         /* if mail file is busy save it in our smtp queue
  494.          * and let the smtp daemon try later.
  495.          */
  496.         if (mlock(Mailspool,ap->val)) {
  497.             addlist(&dlist,ap->val,0);
  498.             fail = queuejob(data,Hostname,dlist,from);
  499.             del_list(dlist);
  500.             dlist = NULLLIST;
  501.         } else {
  502.             char buf[LINELEN];
  503.             int tocnt = 0;
  504.             sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  505. #ifndef    AMIGA
  506.             if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE) {
  507. #else
  508.             if((fp = fopen(mailbox,"r+")) != NULLFILE) {
  509.                 fseek(fp, 0L, 2);
  510. #endif
  511.                 fprintf(fp,"From %s %s",from,ptime(&currtime));
  512.                 host = NULLCHAR;
  513.                 while(fgets(buf,LINELEN,data) != NULLCHAR){
  514.                     if(buf[0] == '\n'){
  515.                         if(tocnt == 0)
  516.                             fprintf(fp,"%s%s\n",Hdrs[APPARTO],ap->val);
  517.                         fputc('\n',fp);
  518.                         break;
  519.                     }
  520.                     fputs(buf,fp);
  521.                     rip(buf);
  522.                     switch(htype(buf)){
  523.                     case TO:
  524.                     case CC:
  525.                         ++tocnt;
  526.                         break;
  527.                     case RRECEIPT:
  528.                         if((cp = getaddress(buf,0)) != NULLCHAR){
  529.                             xfree(host);
  530.                             host = strxdup(cp);
  531.                         }
  532.                         break;
  533.                     }
  534.                 }
  535.                 while((c = fread(buf,1,LINELEN,data)) > 0)
  536.                     if(fwrite(buf,1,c,fp) != c)
  537.                         break;
  538.                 if(ferror(fp))
  539.                     fail = 1;
  540.                 else
  541.                     fputc('\n',fp);
  542.                 /* Leave a blank line between msgs */
  543.                 fclose(fp);
  544. /*
  545.                 addmprompt(ap->val);
  546. */
  547.                 if(Smtpquiet < 2)
  548.                     /* timestamp by dc3sn */
  549.                     tprintf("New mail for %s from <%s> at %s%s",
  550.                         ap->val,from,ctime(&currtime),
  551.                         (Smtpquiet < 1)  ? "\007" : "");
  552.                 if(Smtpquiet == 3)
  553.                     log(-1,"SMTP new mail <%s>",from);
  554.                 if(host != NULLCHAR){
  555.                     rewind(data); /* Send return receipt */
  556.                     mdaemon(data,host,NULLLIST,0);
  557.                     xfree(host);
  558.                 }
  559.             } else {
  560.                 fail = 1;
  561.             }
  562.             rmlock(Mailspool,ap->val);
  563.             if (fail)
  564.                 break;
  565.             smtplog("deliver: To: %s From: %s",ap->val,from);
  566.         }
  567.     }
  568.     return fail;
  569. }
  570.  
  571. /* Return Date/Time in Arpanet format in passed string */
  572. /* daylight changes by dg1zx */
  573. char *
  574. ptime(t)
  575. int32 *t;
  576. {
  577.     /* Print out the time and date field as
  578.      *        "DAY day MONTH year hh:mm:ss ZONE"
  579.      */
  580.     struct tm *ltm;
  581.     static char str[40];
  582.     char tz[5];
  583.  
  584.     /* Read the system time */
  585.     ltm = localtime(t);
  586.  
  587.     /* default environment is set to gmt in main.c ! */
  588.     sprintf(tz," %s", ltm->tm_isdst ? tzname[1] : tzname[0]);
  589.  
  590.     /* rfc 822 format */
  591.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d%.4s\n",
  592.         Days[ltm->tm_wday],
  593.         ltm->tm_mday,
  594.         Months[ltm->tm_mon],
  595.         ltm->tm_year,
  596.         ltm->tm_hour,
  597.         ltm->tm_min,
  598.         ltm->tm_sec,
  599.         tz);
  600.     return(str);
  601. }
  602.  
  603. long
  604. get_msgid()
  605. {
  606.     char sfilename[LINELEN], s[20];
  607.     long sequence = 0;
  608.     FILE *sfile;
  609.  
  610.     sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  611.     /* if sequence file exists, get the value, otherwise set it */
  612.     if((sfile = fopen(sfilename,READ_TEXT)) != NULLFILE) {
  613.         fgets(s,sizeof(s),sfile);
  614.         sequence = atol(s);
  615.         /* Keep it in range of and 8 digit number to use for dos name prefix. */
  616.         if (sequence < 0L || sequence > 99999999L )
  617.             sequence = 0;
  618.         fclose(sfile);
  619.     }
  620.     /* increment sequence number, and write to sequence file */
  621.     if((sfile = open_file(sfilename,WRITE_TEXT,0,1)) != NULLFILE) {
  622.         fprintf(sfile,"%ld",++sequence);
  623.         fclose(sfile);
  624.     }
  625.     return (sequence == 0) ? 1 : sequence;
  626. }
  627.  
  628. #ifdef    MSDOS
  629. /* Illegal characters in a DOS filename */
  630. static char baddoschars[] = "\"[]:|<>+=;,";
  631. #endif
  632.  
  633. /* test if mail address is valid */
  634. int
  635. validate_address(s)
  636. char *s;
  637. {
  638.     char *cp;
  639.     int32 addr;
  640.  
  641.    /*if first character in address is a bang (!) address is NNTP_GATE,dk5dc*/
  642.    if(*s == '!') {
  643. #ifdef NNTP
  644.         return NNTP_GATE;
  645. #else
  646.         return BADADDR;
  647. #endif
  648.     }
  649.     /* if address has @ in it the check dest address */
  650.     if ((cp = strrchr(s,'@')) != NULLCHAR) {
  651.         cp++;
  652.         /* 1st check if its our hostname
  653.         * if not then check the hosts file and see
  654.         * if we can resolve ther address to a know site
  655.         * or one of our aliases
  656.         */
  657.         if (strcmp(cp,Hostname) != 0) {
  658.             if ((addr = mailroute(cp)) == 0 && (Smtpmode & QUEUE) == 0)
  659.                 return BADADDR;
  660.             if (ismyaddr(addr) == NULLIF)
  661.                 return DOMAIN;
  662.         }
  663.  
  664.         /* on a local address remove the host name part */
  665.         *--cp = '\0';
  666.     }
  667.  
  668.     /* if using an external router leave address alone */
  669.     if ((Smtpmode & QUEUE) != 0)
  670.         return LOCAL;
  671.  
  672.     /* check for the user%host hack */
  673.     if ((cp = strrchr(s,'%')) != NULLCHAR) {
  674.         *cp = '@';
  675.         cp++;
  676.         /* reroute based on host name following the % seperator */
  677.         if (mailroute(cp) == 0)
  678.             return BADADDR;
  679.         else
  680.             return DOMAIN;
  681.     }
  682.  
  683. #ifdef MSDOS    /* dos file name checks */
  684.     /* Check for characters illegal in MS-DOS file names */
  685.     for(cp = baddoschars;*cp != '\0';cp++){
  686.         if(strchr(s,*cp) != NULLCHAR)
  687.             return BADADDR;
  688.     }
  689. #endif
  690.     return LOCAL;
  691. }
  692.  
  693. /* place a mail job in the outbound queue */
  694. int
  695. queuejob(dfile,host,to,from)
  696. FILE *dfile;
  697. char *host;
  698. struct list *to;
  699. char *from;
  700. {
  701.     FILE *fp;
  702.     struct list *ap;
  703.     char tmpstring[50], prefix[9], buf[LINELEN];
  704.     int cnt;
  705.  
  706.     sprintf(prefix,"%ld",get_msgid());
  707.     mlock(Mailqdir,prefix);
  708.     sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  709.     if((fp = open_file(tmpstring,WRITE_TEXT,0,1)) == NULLFILE) {
  710.         rmlock(Mailqdir,prefix);
  711.         return 1;
  712.     }
  713.     while((cnt = fread(buf, 1, LINELEN, dfile)) > 0)
  714.         if(fwrite(buf, 1, cnt, fp) != cnt)
  715.             break;
  716.     if(ferror(fp)){
  717.         fclose(fp);
  718.         rmlock(Mailqdir,prefix);
  719.         return 1;
  720.     }
  721.     fclose(fp);
  722.     sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  723.     if((fp = open_file(tmpstring,WRITE_TEXT,0,1)) == NULLFILE) {
  724.         rmlock(Mailqdir,prefix);
  725.         return 1;
  726.     }
  727.     fprintf(fp,"%s\n%s\n",host,from);
  728.     for(ap = to; ap != NULLLIST; ap = ap->next) {
  729.         fprintf(fp,"%s\n",ap->val);
  730.         smtplog("queue job %s To: %s From: %s",prefix,ap->val,from);
  731.     }
  732.     fclose(fp);
  733.     rmlock(Mailqdir,prefix);
  734.     return 0;
  735. }
  736.  
  737. /* Deliver mail to the appropriate mail boxes */
  738. static int near
  739. router_queue(data,from,to)
  740. FILE *data;
  741. char *from;
  742. struct list *to;
  743. {
  744.     int c;
  745.     struct list *ap;
  746.     FILE *fp;
  747.     char tmpstring[50], prefix[9];
  748.  
  749.     sprintf(prefix,"%ld",get_msgid());
  750.     mlock(Routeqdir,prefix);
  751.     sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  752.     if((fp = open_file(tmpstring,WRITE_TEXT,0,1)) == NULLFILE) {
  753.         rmlock(Routeqdir,prefix);
  754.         return 1;
  755.     }
  756.     rewind(data);
  757.     while((c = getc(data)) != EOF)
  758.         if(putc(c,fp) == EOF)
  759.             break;
  760.     if(ferror(fp)){
  761.         fclose(fp);
  762.         rmlock(Routeqdir,prefix);
  763.         return 1;
  764.     }
  765.     fclose(fp);
  766.     sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  767.     if((fp = open_file(tmpstring,WRITE_TEXT,0,1)) == NULLFILE) {
  768.         rmlock(Routeqdir,prefix);
  769.         return 1;
  770.     }
  771.     fprintf(fp,"From: %s\n",from);
  772.     for(ap = to;ap != NULLLIST;ap = ap->next) {
  773.         fprintf(fp,"To: %s\n",ap->val);
  774.     }
  775.     fclose(fp);
  776.     rmlock(Routeqdir,prefix);
  777.     smtplog("rqueue job %s From: %s",prefix,from);
  778.     return 0;
  779. }
  780.  
  781. /* add an element to the front of the list pointed to by head
  782. ** return NULLLIST if out of memory.
  783. */
  784. struct list *
  785. addlist(head,val,type)
  786. struct list **head;
  787. char *val;
  788. int type;
  789. {
  790.     struct list *tp = (struct list *)mxallocw(sizeof(struct list));
  791.  
  792.     tp->next = NULLLIST;
  793.  
  794.     /* allocate storage for the char string */
  795.     tp->val = strxdup(val);
  796.     tp->type = type;
  797.  
  798.     /* add entry to front of existing list */
  799.     if (*head == NULLLIST)
  800.         *head = tp;
  801.     else {
  802.         tp->next = *head;
  803.         *head = tp;
  804.     }
  805.     return tp;
  806.  
  807. }
  808.  
  809. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  810. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  811.  
  812. /* check for and alias and expand alias into a address list */
  813. static struct list * near
  814. expandalias(head, user)
  815. struct list **head;
  816. char *user;
  817. {
  818.     FILE *fp;
  819.     int len, inalias = 0;
  820.     struct list *tp = NULLLIST;
  821.     char *s, *p, buf[LINELEN];
  822.  
  823.     /* no alias file found */
  824.     if ((fp = fopen(Alias,READ_TEXT)) == NULLFILE) {
  825.         /* Try to resolve M* domain name records */
  826.         if((s = resolve_mailb(user)) != NULLCHAR) {
  827.             /* remove the trailing dot */
  828.             len = strlen(s) - 1;
  829.             if(s[len] == '.')
  830.                 s[len] = '\0';
  831.             /* replace first dot with @ if there is no @ */
  832.             if(strchr(s,'@') == NULLCHAR && (p = strchr(s,'.')) != NULLCHAR)
  833.                 *p = '@';
  834.             tp = addlist(head,s,(strchr(s,'@') != NULLCHAR) ? DOMAIN : LOCAL);
  835.             ++inalias;
  836.         }
  837.         return (inalias ? tp : addlist(head,user,LOCAL));
  838.     }
  839.  
  840.     while (fgets(buf,LINELEN,fp) != NULLCHAR) {
  841.         p = buf;
  842.         if ( *p == '#' || *p == '\0')
  843.             continue;
  844.         rip(p);
  845.  
  846.         /* if not in an matching entry skip continuation lines */
  847.         if (!inalias && isspace(*p))
  848.             continue;
  849.  
  850.         /* when processing an active alias check for a continuation */
  851.         if (inalias) {
  852.             if (!isspace(*p))
  853.                 break;    /* done */
  854.         } else {
  855.             s = p;
  856.             SKIPWORD(p);
  857.             *p++ = '\0';    /* end the alias name */
  858.             if (strcmp(s,user) != 0)
  859.                 continue;    /* no match go on */
  860.             inalias = 1;
  861.         }
  862.  
  863.         /* process the recipients on the alias line */
  864.         SKIPSPACE(p);
  865.         while(*p != '\0' && *p != '#') {
  866.             s = p;
  867.             SKIPWORD(p);
  868.             if (*p != '\0')
  869.                 *p++ = '\0';
  870.  
  871.             /* find hostname */
  872. #ifdef NNTP
  873.             if(*s == '!')
  874.                 tp = addlist(head,s,NNTP_GATE);
  875.             else
  876. #endif
  877.               if (strchr(s,'@') != NULLCHAR)
  878.                 tp = addlist(head,s,DOMAIN);
  879.             else
  880.                 tp = addlist(head,s,LOCAL);
  881.             SKIPSPACE(p);
  882.         }
  883.     }
  884.     fclose(fp);
  885.  
  886.     if (inalias) {    /* found and processed and alias. */
  887.         return tp;
  888.     }
  889.     /* no alias found treat as a local address */
  890.     return addlist(head, user, LOCAL);
  891. }
  892.  
  893. #if    defined(ANSIPROTO)
  894. static void near
  895. smtplog(char *fmt, ...)
  896. {
  897.     va_list ap;
  898.     char *cp;
  899.     long t;
  900.     FILE *fp;
  901.  
  902.     if ((fp = open_file(Maillog,APPEND_TEXT,0,1)) == NULLFILE)
  903.         return;
  904.     time(&t);
  905.     cp = ctime(&t);
  906.     rip(cp);
  907.     fprintf(fp,"%s ",cp);
  908.     va_start(ap,fmt);
  909.     vfprintf(fp,fmt,ap);
  910.     va_end(ap);
  911.     fputc('\n',fp);
  912.     fclose(fp);
  913. }
  914.  
  915. #else
  916.  
  917. static void near
  918. smtplog(fmt,arg1,arg2,arg3,arg4)
  919. char *fmt;
  920. int arg1,arg2,arg3,arg4;
  921. {
  922.     char *cp;
  923.     long t;
  924.     FILE *fp;
  925.  
  926.     if ((fp = open_file(Maillog,APPEND_TEXT,0,1)) == NULLFILE)
  927.         return;
  928.     time(&t);
  929.     cp = ctime(&t);
  930.     rip(cp);
  931.     fprintf(fp,"%s ",cp);
  932.     fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  933.     fputc('\n',fp);
  934.     fclose(fp);
  935. }
  936. #endif
  937.  
  938. /* send mail to a single user. Can be called from the ax24 mailbox or
  939. ** from the return mail function in the smtp client
  940. */
  941. static int near
  942. mailuser(data,from,to)
  943. FILE *data;
  944. char *from;
  945. char *to;
  946. {
  947.         int address_type, ret;
  948.         struct list *tolist = NULLLIST;
  949.  
  950.         /* check if address is ok */
  951.         if ((address_type = validate_address(to)) == BADADDR) {
  952.             return 1;
  953.         }
  954.         /* if a local address check for an alias */
  955.         if (address_type == LOCAL)
  956.             expandalias(&tolist, to);
  957.         else
  958.             /* a remote address is added to the list */
  959.             addlist(&tolist, to, address_type);
  960.  
  961.         ret = mailit(data,from,tolist);
  962.         del_list(tolist);
  963.         return ret;
  964.  
  965. }
  966.  
  967. /* Mailer daemon return mail mechanism */
  968. int
  969. mdaemon(data,to,lp,bounce)
  970. FILE *data;        /* pointer to rewound data file */
  971. char *to;        /* Overridden by Errors-To: line if bounce is true */
  972. struct list *lp;    /* error log for failed mail */
  973. int bounce;        /* True for failed mail, otherwise return receipt */
  974. {
  975.     FILE *tfile;
  976.     char buf[LINELEN], *cp, *newto = NULLCHAR;
  977.     int cnt;
  978.  
  979.     if(to == NULLCHAR || (to != NULLCHAR && *to == '\0') || bounce){
  980.         while(fgets(buf,sizeof(buf),data) != NULLCHAR) {
  981.             if(*buf == '\n')
  982.                 break;
  983.             /* Look for Errors-To: */
  984.             if(htype(buf) == ERRORSTO &&
  985.                (cp = getaddress(buf,0)) != NULLCHAR){
  986.                 xfree(newto);
  987.                 newto = strxdup(cp);
  988.                 break;
  989.             }
  990.         }
  991.         if(newto == NULLCHAR && ((to != NULLCHAR && *to == '\0') ||
  992.            to == NULLCHAR))
  993.             return -1;
  994.         rewind(data);
  995.     }
  996.     if((tfile = temp_file(0,1)) == NULLFILE)
  997.         return -1;
  998.     fprintf(tfile,"%s%s",Hdrs[DATE],ptime(&currtime));
  999.     fprintf(tfile,"%s<%ld@%s>\n",Hdrs[MSGID],get_msgid(),Hostname);
  1000.     fprintf(tfile,"%sMAILER-DAEMON@%s (Mail Delivery Subsystem)\n",
  1001.         Hdrs[FROM],Hostname);
  1002.     fprintf(tfile,"%s%s\n",Hdrs[TO],newto != NULLCHAR ? newto : to);
  1003.     fprintf(tfile,"%s%s\n\n",Hdrs[SUBJECT],
  1004.         bounce ? "Failed mail" : "Return receipt");
  1005.     if(bounce) {
  1006.         fputs("  ==== transcript follows ====\n\n",tfile);
  1007.         for (; lp != NULLLIST; lp = lp->next)
  1008.             fprintf(tfile,"%s\n",lp->val);
  1009.         fputs("\n  ==== Unsent message follows ====\n",tfile);
  1010.     } else {
  1011.         fputs("  ==== Message header follows ====\n",tfile);
  1012.     }
  1013.     while(fgets(buf,sizeof(buf),data) != NULLCHAR){
  1014.         if(*buf == '\n')
  1015.             break;
  1016.         fputs(buf,tfile);
  1017.     }
  1018.     if(bounce){
  1019.         fputc('\n',tfile);
  1020.         while((cnt = fread(buf,1,sizeof(buf),data)) > 0)
  1021.             fwrite(buf,1,cnt,tfile);
  1022.     }
  1023.     fseek(tfile,0L,0);
  1024.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  1025.     mailuser(tfile,"",newto != NULLCHAR ? newto : to);
  1026.     fclose(tfile);
  1027.     xfree(newto);
  1028.     return 0;
  1029. }
  1030.